#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <iostream>

#include "nlohmann/json.hpp"
using std::cout;
using std::string;
using std::wstring;
using json = nlohmann::json;

#include <windows.h>

namespace Clicker {
inline std::wstring s2w(const std::string &str) { // 将string转换成wstring
    if (str.empty())
        return L"";

    int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
                                          (int)str.size(), nullptr, 0);
    std::wstring result(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &result[0],
                        size_needed);
    return result;
}

inline std::string w2s(const std::wstring &wstr) { // 将wstring转换成string
    if (wstr.empty())
        return "";

    int size_needed =
        WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), nullptr,
                            0, nullptr, nullptr);
    std::string result(size_needed, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &result[0],
                        size_needed, nullptr, nullptr);
    return result;
}

// 注意：操作过程中可能会出现string和wstring混合
// 但是保持一个原则：外部访问以及内部最终存储都应该使用wstring来保持和Win32
// API兼容
class JsonConfig {
  private:
    json cfgJson;
    string location;
    wstring content;

  public:
    JsonConfig(string location) { // 指定配置文件位置，如果不存在则会创建
        // 读取文件，存储到宽字符串content
        this->location = location;
        readConfig();
    }

    // 如果配置出错，就向硬盘中写入默认的配置
    JsonConfig(string location, json defaultConfig) {
        this->location = location;
        if (readConfig() == -1) {
            setJson(defaultConfig);
            saveConfig();
            readConfig();
        };
    }

    wstring getStrCont() { return this->content; }     // 返回宽字符串
    nlohmann::json getJson() { return this->cfgJson; } // 返回Json对象

    int setJson(nlohmann::json inpJson) { // 传入一个Json，覆盖对象内的值
        this->cfgJson = inpJson;
        this->content = s2w(this->cfgJson.dump(4)); // 同时修改字符串
        return 0;
    }

    int readConfig() { // 从磁盘中读取
        FILE *pf;
        fopen_s(&pf, location.data(), "r, ccs=UTF-8");
        if (pf != nullptr) {
            std::vector<wchar_t> readBfr(
                512, '\0'); // 用vector与传统的文件管理兼容，然后转换成wstring
            fread(readBfr.data(), sizeof(wchar_t), 512, pf);
            this->content.assign(readBfr.data(), readBfr.size());
            fclose(pf);
            this->cfgJson = json::parse(content);
            return 0;
        } else {
            this->content = L"{}";
            this->cfgJson = json::parse(content);
            saveConfig();
            return -1;
        }
    }

    int saveConfig() { // 将更改写入磁盘
        FILE *pf;
        fopen_s(&pf, location.data(), "w, ccs=UTF-8");
        if (pf != nullptr) {
            fwrite(content.data(), sizeof(wchar_t), content.size(), pf);
            fclose(pf);
        } else {
            return 1;
        }
        return 0;
    }

    // 不同类型的获得配置数据
    template <typename T> T getConfig(const wstring &wkey) {
        string skey = w2s(wkey);
        auto val = this->cfgJson.at(skey);
        return val.get<T>();
    }
    wstring getConfig(const wstring &wkey) {
        string skey = w2s(wkey);
        auto val = this->cfgJson.at(skey).get<string>(); // 显式类型转换为string
        return s2w(val);                                 // 返回宽字符串
    }

    // 修改配置数据
    template <typename T> int setConfig(const wstring &wkey, T value) {
        string skey = w2s(wkey);
        this->cfgJson[skey] = value;
        this->content = s2w(this->cfgJson.dump(4)); // 同时修改字符串
        return 0;
    }
    int setConfig(const wstring &wkey, const wstring wval) {
        string skey = w2s(wkey);
        string sval = w2s(wval);
        this->cfgJson[skey] = sval;
        this->content = s2w(this->cfgJson.dump(4)); // 同时修改字符串
        return 0;
    }
};
} // namespace Clicker